import { Ref, reactive, computed, onMounted, onUnmounted, watch } from "vue";

interface VirtualScrollItem {
  index: number; // 元素索引
  height: number; // 元素高度
  offset: number; // 元素偏移量
  measured: boolean; // 是否已测量
}

interface VirtualScrollOptions<T> {
  wrap: Ref<HTMLElement>;
  data: T[]; // 数据源
  estimateHeight: number; // 预估高度
  buffer: number; // 缓冲区大小
  getKey?: (item: T) => string | number; // 自定义 key 提取函数
}

interface VisibleItem<T> {
  key: string | number; // 唯一标识
  data: T; // 数据项
  offset: number; // 偏移量
}

export function useVirtualScroll<T>(options: VirtualScrollOptions<T>) {
  const { wrap } = options;

  const state = reactive({
    positions: [] as VirtualScrollItem[], // 位置信息
    totalHeight: 0, // 总高度
    scrollTop: 0, // 滚动位置
    viewportHeight: 0, // 视口高度
    measuredIndices: new Set<number>() // 已测量的索引
  });

  // 初始化位置数组
  const initPositions = () => {
    state.positions = options.data.map((_, index) => ({
      index,
      height: options.estimateHeight,
      offset: index === 0 ? 0 : state.positions[index - 1].offset + state.positions[index - 1].height,
      measured: false
    }));
    updateTotalHeight();
  };

  // 更新总高度
  const updateTotalHeight = () => {
    const last = state.positions[state.positions.length - 1];
    state.totalHeight = last ? last.offset + last.height : 0;
  };

  // 计算可见数据
  const autoVirtualData = computed(() => {
    const startIdx = findStartIndex(state.scrollTop);
    const endIdx = findEndIndex(state.scrollTop + state.viewportHeight);

    const start = Math.max(0, startIdx - options.buffer);
    const end = Math.min(options.data.length - 1, endIdx + options.buffer);

    return state.positions.slice(start, end + 1).map(pos => ({
      key: options.getKey ? options.getKey(options.data[pos.index]) : pos.index,
      data: options.data[pos.index],
      offset: pos.offset
    }));
  });

  // 二分查找起始索引
  const findStartIndex = (scrollTop: number) => {
    let left = 0,
      right = state.positions.length - 1;
    while (left <= right) {
      const mid = (left + right) >> 1;
      const pos = state.positions[mid];
      pos.offset + pos.height > scrollTop ? (right = mid - 1) : (left = mid + 1);
    }
    return left;
  };

  // 二分查找结束索引
  const findEndIndex = (scrollBottom: number) => {
    let left = 0,
      right = state.positions.length - 1;
    while (left <= right) {
      const mid = (left + right) >> 1;
      const pos = state.positions[mid];
      pos.offset < scrollBottom ? (left = mid + 1) : (right = mid - 1);
    }
    return right;
  };

  // 处理滚动事件
  const handleScroll = (e: Event) => {
    state.scrollTop = (e.target as HTMLElement).scrollTop;
    updateViewport();
  };

  // 更新视口高度
  const updateViewport = () => {
    if (wrap.value) {
      state.viewportHeight = wrap.value.clientHeight;
    }
  };

  // 动态测量逻辑
  const setItemRef = (el: HTMLElement | null) => {
    if (!el) return;

    const index = parseInt(el.dataset.index!);
    if (state.measuredIndices.has(index)) return;

    const actualHeight = el.clientHeight;
    if (actualHeight !== state.positions[index].height) {
      updatePosition(index, actualHeight);
    }
    state.measuredIndices.add(index);
  };

  // 更新位置信息
  const updatePosition = (index: number, height: number) => {
    const oldHeight = state.positions[index].height;
    if (oldHeight === height) return;

    state.positions[index].height = height;
    state.positions[index].measured = true;

    let currentOffset = index > 0 ? state.positions[index - 1].offset + state.positions[index - 1].height : 0;

    for (let i = index; i < state.positions.length; i++) {
      state.positions[i].offset = currentOffset;
      currentOffset += state.positions[i].height;
      if (i > index && state.positions[i].measured) break;
    }

    updateTotalHeight();
  };

  // 监听容器尺寸变化
  let resizeObserver: ResizeObserver | null = null;
  onMounted(() => {
    initPositions();
    updateViewport();

    resizeObserver = new ResizeObserver(() => {
      updateViewport();
      initPositions();
    });
    if (wrap.value) {
      resizeObserver.observe(wrap.value);
    }
  });

  onUnmounted(() => {
    resizeObserver?.disconnect();
  });

  // 监听数据变化
  watch(
    () => [...options.data],
    () => {
      initPositions();
      state.measuredIndices.clear();
    }
  );

  return {
    wrap,
    autoVirtualData: autoVirtualData as Readonly<VisibleItem<T>[]>,
    totalHeight: computed(() => state.totalHeight),
    setItemRef,
    handleScroll
  };
}
